package de.hft_stuttgart.dsa.ctt.validation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.hft_stuttgart.dsa.ctt.model.AbstractMatrix;
import de.hft_stuttgart.dsa.ctt.model.Course;
import de.hft_stuttgart.dsa.ctt.model.Curriculum;
import de.hft_stuttgart.dsa.ctt.model.Period;
import de.hft_stuttgart.dsa.ctt.model.ProblemInstance;
import de.hft_stuttgart.dsa.ctt.model.UnavailabilityConstraint;

/**
 * @author Matthias Huber - MatthewHuber@web.de
 */
public class FeasibleSolutionChecker {

	private AbstractMatrix matrix;

	private ProblemInstance problemInstance;

	public FeasibleSolutionChecker(AbstractMatrix matrix, ProblemInstance problemInstance) {
		this.matrix = matrix;
		this.problemInstance = problemInstance;
	}

	/**
	 * @return True if solution is feasible otherwise false.
	 */
	public boolean checkSolution() {
		Course[][] solutionMatrix = (Course[][]) matrix.getPeriodRoomMatrix();
		// first: check if there exists a constraint based on the
		// UnavailabilityConstraints
		Map<String, Integer> coursesMap = new HashMap<String, Integer>();
		int noConstraints = 0; 
		for (Course course : problemInstance.getAllCourses().values()) {
			coursesMap.put(course.getCourseId(), 0);
			for (UnavailabilityConstraint constraint : course.getUnavailabilityConstraints()) {
				noConstraints++;
				String counstraintCourseId = constraint.getCourseId();
				int unavailabilityPeriod = Period.getCalculatedPeriod(problemInstance.getPeriodsPerDay(), constraint.getPeriodOfDay(), constraint.getDayOfWeek());
				// search through the period and check if the course is assigned
				for (int i = 0; i < solutionMatrix[unavailabilityPeriod].length; ++i) {
					Course assignedCourse = (Course) solutionMatrix[unavailabilityPeriod][i];
//					System.out.println(counstraintCourseId + " " + constraint.getDayOfWeek() + " " + constraint.getPeriodOfDay() + " == " + assignedCourse.getCourseId());
					if (counstraintCourseId.equals(assignedCourse.getCourseId())) {
						System.out.println("CAUSE: UNAVAILABILITY");
						return false;
					}
				}
//				System.out.println("-------------------------------------------");
			}
		}
//		System.out.println("# of Constraints: " + noConstraints);

		// second: check if a course is assigned double to a period OR a teacher
		// is assigned double
		for (int i = 0; i < solutionMatrix.length; ++i) {
			List<Course> courses = new ArrayList<Course>(solutionMatrix[i].length);
			List<String> teachers = new ArrayList<String>(solutionMatrix[i].length);
			for (int j = 0; j < solutionMatrix[i].length; ++j) {
				Course course = (Course) solutionMatrix[i][j];
				// if course is already assigned to a room then its not a
				// feasible solution
				if (course.getCourseId() != null) {
					String teacher = course.getTeacher();
					if (courses.contains(course) || teachers.contains(teacher)) {
						System.out.println("CAUSE: COURSE DOUBLE ASSIGNED OR TEACHER");
						System.out.println(teacher + " == " + teachers.get(teachers.indexOf(teacher)));
						return false;
					}
					courses.add(course);
					teachers.add(teacher);
					// aggregate the assigned lectures of this course
					coursesMap.put(course.getCourseId(), coursesMap.get(course.getCourseId()) + 1);
				}
			}
		}

		// third: test if all number of lectures are assigned
		int counter = 1;
		for (Course course : problemInstance.getAllCourses().values()) {
			if (coursesMap.get(course.getCourseId()) != course.getNumberOfLectures()) {
				return false;
			}
//			System.out.println("checked" + counter);
			++counter;
		}

		// forth: check if courses of one curriculum are assigned to the same
		// period
		for (Curriculum curriculum : problemInstance.getCurricula().getCurricula()) {
			List<Course> courses = curriculum.getCourses();
			for (int i = 0; i < solutionMatrix.length; ++i) {
				int appearances = 0; // must be less or equal to one;
				for (int j = 0; j < solutionMatrix[i].length; ++j) {
					Course course = solutionMatrix[i][j];
					if (null != course.getCourseId() && courses.contains(course)) {
						++appearances;
						if (appearances > 1) {
							System.out.println("CAUSE: # LECTURES");
							return false;
						}
					}
				}
			}
		}

		return true;
	}
}
